#include "flow_seq_manager.h"
#include "persister.h"
#include "log.h"

#include "z_base_api.h"


struct flow_seq_manager_s {
    z_hash_t * persister_map_;
    z_hash_t * flow_seq_map_;
};

void flow_seq_manager_destroy(flow_seq_manager_t * mgr) {
    if (mgr) {
        if (mgr->persister_map_) {
            z_pair_t * p = NULL;
            persister_t * persister = NULL;
            while (NULL != (p = zH_foreach(mgr->persister_map_, p))){
                persister = (persister_t *)(obj2ll(p->value_));
                persister_destroy(persister);
            }
            zH_free(mgr->persister_map_);
            mgr->persister_map_ = NULL;
        }

        if (mgr->flow_seq_map_) {
            zH_free(mgr->flow_seq_map_);
            mgr->flow_seq_map_ = NULL;
        }

        free(mgr);
    }
}

flow_seq_manager_t * flow_seq_manager_create(void) {
    flow_seq_manager_t * mgr = (flow_seq_manager_t *)calloc(1, sizeof(flow_seq_manager_t));
    if (!mgr) {
        ERROR_PRINT("Calloc flow_seq_manager failed");
        return NULL;
    }

    mgr->persister_map_ = zH_init();
    if (!(mgr->persister_map_)) {
        ERROR_PRINT("Init persist map failed");
        goto ERROR;
    }

    mgr->flow_seq_map_ = zH_init();
    if (!(mgr->flow_seq_map_)) {
        ERROR_PRINT("Init flow seq map failed");
        goto ERROR;
    }

    return mgr;

ERROR:
    flow_seq_manager_destroy(mgr);
    return NULL;
}

#define MAX_SUFFIX_LEN  50  
int flow_seq_manager_add_with_mode(flow_seq_manager_t * mgr, int flow_type, const char * file_name_prefix, int mode) {
    if ((!mgr) || (!(mgr->persister_map_)) || (!(mgr->flow_seq_map_)) || (!file_name_prefix)) {
        ERROR_PRINT("Input args illegal: mgr, persister_map, flow_seq_map or file_name_prefix is NULL");
        return -1;
    }

    long long lv =0;
    if (NULL != zH_llfind_ll(mgr->persister_map_, flow_type, &lv)) {
        ERROR_PRINT("flow_type[%d] already exist in persister map", flow_type);
        return -1;
    }

    if (NULL != zH_llfind_ll(mgr->flow_seq_map_, flow_type, &lv)) {
        ERROR_PRINT("flow_type[%d] persister map not exist but already exist in flow seq map", flow_type);
        return -1;
    }

    int max_file_name_len = strlen(file_name_prefix) + MAX_SUFFIX_LEN;
    char * full_file_name = (char *)calloc(1, max_file_name_len);
    if (full_file_name) {
        snprintf(full_file_name, max_file_name_len, "%s_%d", file_name_prefix, flow_type);
    }
    else {
        ERROR_PRINT("calloc full file name failed");
        return -1;
    }

    persister_t * persister = persister_create_with_mode(full_file_name, mode);
    if (!persister) {
        ERROR_PRINT("Create persister failed with file[%s], mode[%d]", full_file_name, mode);
        free(full_file_name);
        return -1;
    }
    free(full_file_name);

    unsigned long long seq_no = 0;
    if (-1 == persister_load(persister, &seq_no)) {
        ERROR_PRINT("Load seq no from file failed");
        return -1;  
    }
    INFO_PRINT("Flow type[%d] init seq_no[%U] from persister", flow_type, seq_no);

    zH_llinsert_ll(mgr->flow_seq_map_, flow_type, seq_no);
    zH_llinsert_ll(mgr->persister_map_, flow_type, (long long)persister);
    return 0;
}

int flow_seq_manager_add(flow_seq_manager_t * mgr, int flow_type, const char * file_name_prefix) {
    return flow_seq_manager_add_with_mode(mgr, flow_type, file_name_prefix, MODE_STR);
}

int flow_seq_manager_del(flow_seq_manager_t * mgr, int flow_type) {
    if ((!mgr) || (!(mgr->persister_map_)) || (!(mgr->flow_seq_map_))) {
        ERROR_PRINT("Input args illegal: mgr, persister_map or flow_seq_map is NULL");
        return -1;
    }

    int ret = 0;
    long long lv =0;
    zH_llfind_ll(mgr->persister_map_, flow_type, &lv);
    if(0 != zH_delete_by_ll(mgr->persister_map_, flow_type)){
        ERROR_PRINT("flow_type[%d] not exist in persist map", flow_type);
        ret = -1;
    }
    else {
        persister_destroy((persister_t *)lv);
    }

    if(0 != zH_delete_by_ll(mgr->flow_seq_map_, flow_type)){
        ERROR_PRINT("flow_type[%d] not exist in flow seq map", flow_type);
        ret = -1;
    }

    return ret;
}

static int get_seq_no(flow_seq_manager_t * mgr, int flow_type, unsigned long long * seq_no) {
    if ((!mgr) || (!(mgr->flow_seq_map_))) {
        ERROR_PRINT("Input args illegal: mgr or flow seq map is NULL");
        return -1;
    }
    
    if (NULL == zH_llfind_ll(mgr->flow_seq_map_, flow_type, (long long *)seq_no)) {
        ERROR_PRINT("FLow type[%d] not find in flow seq map", flow_type);
        return -1;
    }

    return 0;
}

int flow_seq_manager_set_seq_no(flow_seq_manager_t * mgr, int flow_type, unsigned long long seq_no) {
    unsigned long long lv =0;
    if (0 == get_seq_no(mgr, flow_type, &lv)) {
        return zH_replace(mgr->flow_seq_map_, (z_obj_t *)zLL_init_ll(flow_type), (z_obj_t *)zLL_init_ll(seq_no));
    }
    else {
        ERROR_PRINT("Flow type[%d] set flow seq failed", flow_type);
        return -1;
    }
}

unsigned long long flow_seq_manager_get_seq_no(flow_seq_manager_t * mgr, int flow_type) {
    unsigned long long seq_no = 0;
    if (0 == get_seq_no(mgr, flow_type, &seq_no)) {
        return seq_no;
    }
    else {
        ERROR_PRINT("Flow type[%d] get flow seq failed", flow_type);
        return 0;
    }
}

static persister_t * get_persister(flow_seq_manager_t * mgr, int flow_type) {
    if ((!mgr) || (!(mgr->persister_map_))) {
        ERROR_PRINT("Input args illegal: mgr or persister map is NULL");
        return NULL;
    }

    long long lv =0;
    if (NULL == zH_llfind_ll(mgr->persister_map_, flow_type, &lv)) {
        ERROR_PRINT("Can't find the persister which flow type is %d", flow_type);
        return NULL;
    }

    return (persister_t *)lv;
}

int flow_seq_manager_save(flow_seq_manager_t * mgr, int flow_type) {
    persister_t * persister = get_persister(mgr, flow_type);
    if (persister) {
        unsigned long long seq_no =  flow_seq_manager_get_seq_no(mgr, flow_type);
        return  persister_save(persister, seq_no);
    }
    else {
        ERROR_PRINT("Flow type[%d] save flow seq failed", flow_type);
        return -1;
    }

}

int flow_seq_manager_load(flow_seq_manager_t * mgr, int flow_type, unsigned long long * seq_no)  {
    persister_t * persister = get_persister(mgr, flow_type); 
    if (persister) {
        return persister_load(persister, seq_no);
    }
    else {
        ERROR_PRINT("Flow type[%d] load flow seq failed", flow_type);
        return 0;
    }
}

